<h1>控制器介绍</h1>

<blockquote>
    <blockquote>
        <p>提示：在 v1.6 中，此文档所涉及的 API 有重大变更，<code>this.ServeJson()</code> 更改为 <code>this.ServeJSON()</code>，<code>this.TplNames</code> 更改为 <code>this.TplName</code>。</p>
    </blockquote>
</blockquote>

<p>基于 beego 的 Controller 设计，只需要匿名组合 <code>beego.Controller</code> 就可以了，如下所示：</p>

<pre><code>type xxxController struct {
    beego.Controller
}
</code></pre>

<p><code>beego.Controller</code> 实现了接口 <code>beego.ControllerInterface</code>，<code>beego.ControllerInterface</code> 定义了如下函数：</p>

<ul>
    <li><p>Init(ct *context.Context, childName string, app interface{})</p>

        <p>这个函数主要初始化了 Context、相应的 Controller 名称，模板名，初始化模板参数的容器 Data，app 即为当前执行的 Controller 的 reflecttype，这个 app 可以用来执行子类的方法。</p></li>

    <li><p>Prepare()</p>

        <p>这个函数主要是为了用户扩展用的，这个函数会在下面定义的这些 Method 方法之前执行，用户可以重写这个函数实现类似用户验证之类。</p></li>

    <li><p>Get()</p>

        <p>如果用户请求的 HTTP Method 是 GET，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Get 请求。</p></li>

    <li><p>Post()</p>

        <p>如果用户请求的 HTTP Method 是 POST，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Post 请求。</p></li>

    <li><p>Delete()</p>

        <p>如果用户请求的 HTTP Method 是 DELETE，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Delete 请求。</p></li>

    <li><p>Put()</p>

        <p>如果用户请求的 HTTP Method 是 PUT，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Put 请求.</p></li>

    <li><p>Head()</p>

        <p>如果用户请求的 HTTP Method 是 HEAD，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Head 请求。</p></li>

    <li><p>Patch()</p>

        <p>如果用户请求的 HTTP Method 是 PATCH，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Patch 请求.</p></li>

    <li><p>Options()</p>

        <p>如果用户请求的HTTP Method是OPTIONS，那么就执行该函数，默认是 405，用户继承的子 struct 中可以实现了该方法以处理 Options 请求。</p></li>

    <li><p>Finish()</p>

        <p>这个函数是在执行完相应的 HTTP Method 方法之后执行的，默认是空，用户可以在子 struct 中重写这个函数，执行例如数据库关闭，清理数据之类的工作。</p></li>

    <li><p>Render() error</p>

        <p>这个函数主要用来实现渲染模板，如果 beego.AutoRender 为 true 的情况下才会执行。</p></li>
</ul>

<p>所以通过子 struct 的方法重写，用户就可以实现自己的逻辑，接下来我们看一个实际的例子：</p>

<pre><code>type AddController struct {
    beego.Controller
}

func (this *AddController) Prepare() {

}

func (this *AddController) Get() {
    this.Data[&quot;content&quot;] = &quot;value&quot;
    this.Layout = &quot;admin/layout.html&quot;
    this.TplName = &quot;admin/add.tpl&quot;
}

func (this *AddController) Post() {
    pkgname := this.GetString(&quot;pkgname&quot;)
    content := this.GetString(&quot;content&quot;)
    pk := models.GetCruPkg(pkgname)
    if pk.Id == 0 {
        var pp models.PkgEntity
        pp.Pid = 0
        pp.Pathname = pkgname
        pp.Intro = pkgname
        models.InsertPkg(pp)
        pk = models.GetCruPkg(pkgname)
    }
    var at models.Article
    at.Pkgid = pk.Id
    at.Content = content
    models.InsertArticle(at)
    this.Ctx.Redirect(302, &quot;/admin/index&quot;)
}
</code></pre>

<p>从上面的例子可以看出来，通过重写方法可以实现对应 method 的逻辑，实现 RESTful 结构的逻辑处理。</p>

<p>下面我们再来看一种比较流行的架构，首先实现一个自己的基类 baseController，实现一些初始化的方法，然后其他所有的逻辑继承自该基类：</p>

<pre><code>type NestPreparer interface {
        NestPrepare()
}

// baseRouter implemented global settings for all other routers.
type baseController struct {
        beego.Controller
        i18n.Locale
        user    models.User
        isLogin bool
}
// Prepare implemented Prepare method for baseRouter.
func (this *baseController) Prepare() {

        // page start time
        this.Data[&quot;PageStartTime&quot;] = time.Now()

        // Setting properties.
        this.Data[&quot;AppDescription&quot;] = utils.AppDescription
        this.Data[&quot;AppKeywords&quot;] = utils.AppKeywords
        this.Data[&quot;AppName&quot;] = utils.AppName
        this.Data[&quot;AppVer&quot;] = utils.AppVer
        this.Data[&quot;AppUrl&quot;] = utils.AppUrl
        this.Data[&quot;AppLogo&quot;] = utils.AppLogo
        this.Data[&quot;AvatarURL&quot;] = utils.AvatarURL
        this.Data[&quot;IsProMode&quot;] = utils.IsProMode

        if app, ok := this.AppController.(NestPreparer); ok {
                app.NestPrepare()
        }
}
</code></pre>

<p>上面定义了基类，大概是初始化了一些变量，最后有一个 Init 函数中那个 app 的应用，判断当前运行的 Controller 是否是 NestPreparer 实现，如果是的话调用子类的方法，下面我们来看一下 NestPreparer 的实现：</p>

<pre><code>type BaseAdminRouter struct {
    baseController
}

func (this *BaseAdminRouter) NestPrepare() {
    if this.CheckActiveRedirect() {
            return
    }

    // if user isn't admin, then logout user
    if !this.user.IsAdmin {
            models.LogoutUser(&amp;this.Controller)

            // write flash message
            this.FlashWrite(&quot;NotPermit&quot;, &quot;true&quot;)

            this.Redirect(&quot;/login&quot;, 302)
            return
    }

    // current in admin page
    this.Data[&quot;IsAdmin&quot;] = true

    if app, ok := this.AppController.(ModelPreparer); ok {
            app.ModelPrepare()
            return
    }
}

func (this *BaseAdminRouter) Get(){
    this.TplName = &quot;Get.tpl&quot;
}

func (this *BaseAdminRouter) Post(){
    this.TplName = &quot;Post.tpl&quot;
}
</code></pre>

<p>这样我们的执行器执行的逻辑是这样的，首先执行 Prepare，这个就是 Go 语言中 struct 中寻找方法的顺序，依次往父类寻找。执行 <code>BaseAdminRouter</code> 时，查找他是否有 <code>Prepare</code> 方法，没有就寻找 <code>baseController</code>，找到了，那么就执行逻辑，然后在 <code>baseController</code> 里面的 <code>this.AppController</code> 即为当前执行的控制器 <code>BaseAdminRouter</code>，因为会执行 <code>BaseAdminRouter.NestPrepare</code> 方法。然后开始执行相应的 Get 方法或者 Post 方法。</p>

<h2>提前终止运行</h2>

<p>我们应用中经常会遇到这样的情况，在 Prepare 阶段进行判断，如果用户认证不通过，就输出一段信息，然后直接中止进程，之后的 Post、Get 之类的不再执行，那么如何终止呢？可以使用 <code>StopRun</code> 来终止执行逻辑，可以在任意的地方执行。</p>

<pre><code>type RController struct {
    beego.Controller
}

func (this *RController) Prepare() {
    this.Data[&quot;json&quot;] = map[string]interface{}{&quot;name&quot;: &quot;astaxie&quot;}
    this.ServeJSON()
    this.StopRun()
}
</code></pre>

<blockquote>
    <blockquote>
        <blockquote>
            <p>调用 StopRun 之后，如果你还定义了 Finish 函数就不会再执行，如果需要释放资源，那么请自己在调用 StopRun 之前手工调用 Finish 函数。</p>
        </blockquote>
    </blockquote>
</blockquote>

<h2>在表单中使用 PUT 方法</h2>

<p>首先要说明, 在 XHTML 1.x 标准中, 表单只支持 GET 或者 POST 方法. 虽然说根据标准, 你不应该将表单提交到 PUT 方法, 但是如果你真想的话, 也很容易, 通常可以这么做:</p>

<p>首先表单本身还是使用 POST 方法提交, 但是可以在表单中添加一个隐藏字段:</p>

<pre><code>&lt;form method=&quot;post&quot; ...&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;_method&quot; value=&quot;put&quot; /&gt;
</code></pre>

<p>接着在 Beego 中添加一个过滤器来判断是否将请求当做 PUT 来解析:</p>

<pre lang="go"><code>var FilterMethod = func(ctx *context.Context) {
    if ctx.BeegoInput.Query(&quot;_method&quot;)!=&quot;&quot; &amp;&amp; ctx.BeegoInput.IsPost(){
          ctx.Request.Method = ctx.BeegoInput.Query(&quot;_method&quot;)
    }
}

beego.InsertFilter(&quot;*&quot;, beego.BeforeRouter, FilterMethod)
</code></pre>